#include "defines.h"
#include "netdef.h"
#include "ip.h"
#include "kozos.h"
#include "lib.h"
#include "netlib.h"
#include "icmp.h"
#include "ping.h"

static kz_msgbox_id_t icmp_mbox = MSGBOX_NULL;

void icmp_init(void)
{
    icmp_mbox = kz_getmsgbox();
}

void handle_icmp(uint8 *packet)
{
    ASSERT(icmp_mbox != MSGBOX_NULL, "no icmp_mbox");
    if (kz_send(icmp_mbox, 'h', (char *)packet, 0, NET_MBOXSIZE) == -1)
        kz_kmfree(packet);
}

static void handle_icmp_packet(uint8 *buffer)
{
    ethernet_header *ehdr = (ethernet_header *)buffer;
    ip_header *ihdr = (ip_header *)(ehdr + 1);
    ping_header *phdr = (ping_header *)(ihdr + 1);
    uint16 data_len = ihdr->ip_total_length - sizeof(*ihdr);

    if (data_len < sizeof(*phdr) ||
        !(phdr->icmp_type == 0 || phdr->icmp_type == 8) ||
        phdr->icmp_code != 0 ||
        ip_checksum((uint8 *)phdr, data_len) != 0)
    {
        kz_kmfree(buffer); // ECHO以外は捨てる
        return;
    }

    if (phdr->icmp_type == 0) {

        ping_handle(buffer);

    } else {

        uint8 dst_IP[4];

        ASSERT(phdr->icmp_type == 8, "icmp_type error");

        phdr->icmp_type = 0;
        phdr->icmp_checksum = 0;
        phdr->icmp_checksum = ip_checksum((uint8 *)phdr, data_len);

        memcpy(dst_IP, ihdr->ip_src_IP, 4);

        if (is_loopback_address(ihdr->ip_dst_IP)) {
            // 127.～を受信パケットと同じにしたい
            uint8 src_IP[4];
            memcpy(src_IP, ihdr->ip_dst_IP, 4);
            ip_send(buffer, data_len, IP_PROTOCOL_ICMP, dst_IP, src_IP);
        } else {
            // specific-destination addressが送信元になるはず(NICが1つなので)
            ip_send(buffer, data_len, IP_PROTOCOL_ICMP, dst_IP, NULL);
        }
    }
}

void icmp_main(int argc, char *argv)
{
    int cmd;
    char *p;

    while (1) {
        kz_recv(icmp_mbox, &cmd, &p);

        if (cmd == 'h') {
            handle_icmp_packet((uint8 *)p);
        } else {
            ASSERT(0, "icmp: Bad cmd");
        }
    }
}
